/*
 * @Author jiangzhiwei
 * @Date 2023-07-25 17:57:23
 * @LastEditors jiangzhiwei
 * @LastEditTime 2023-07-31 02:33:56
 * @FilePath \test-cron\src\components\yc-cron\utils\time.js
 * @Description 时间相关
 */
/**
 * @Author jiangzhiwei
 * @Date 2023-07-25 17:57:52
 * @Description 指定时间所在月份的最后一天是几号
 * @param {object} time 指定时间。dayjs对象
 * @return {number} 几号
 */
export function getLastDayOfMonth (time) {
  return time.daysInMonth()
}

/**
 * @Author jiangzhiwei
 * @Date 2023-07-30 19:59:57
 * @Description 指定时间所在月份的第 一个星期几是几号
 * @param {object} time 指定时间。dayjs对象
 * @param {number} dayOfWeek 星期几。0-6代表周日-周六
 * @return {number} 几号
 */
export function getFirstWeekOfMonth (time, dayOfWeek) {
  const firstDate = time.startOf('month') // 该月第一天
  let day = firstDate.day(dayOfWeek) // 第一天所在周的星期几
  if (time.month() !== day.month()) { // 如果跨月了，则加7天
    day = day.add(7, 'day')
  }
  return day.date()
}

/**
 * @Author jiangzhiwei
 * @Date 2023-07-25 18:25:18
 * @Description 指定时间所在月份的最后一个星期几是几号
 * @param {object} time 指定时间。dayjs对象
 * @param {number} dayOfWeek 星期几。0-6代表周日-周六
 * @return {number} 几号
 */
export function getLastWeekOfMonth (time, dayOfWeek) {
  const lastDate = time.endOf('month') // 该月最后一天
  let day = lastDate.day(dayOfWeek) // 最后一天所在周的星期几
  if (time.month() !== day.month()) { // 如果跨月了，则减7天
    day = day.subtract(7, 'day')
  }
  return day.date()
}

/**
 * @Author jiangzhiwei
 * @Date 2023-07-26 09:49:19
 * @Description 离指定时间所在月份的几号最近的工作日。如果该月没有指定日这一天，则返回undefined
 * @param {object} time 指定时间。dayjs对象
 * @param {number} date 指定日
 * @return {number | undefined}
 */
export function getLatestWeekdayOfMonth (time, date) {
  const total = getLastDayOfMonth(time) // 该月总天数
  if (date > total) { // 表示该月没有这一天
    return undefined
  }
  const theDate = time.date(date) // 几号对应日期
  let dayOfWeek = theDate.day() // 星期
  if (dayOfWeek === 0) { // 周日
    const monday = theDate.add(1, 'day')
    if (monday.month() === time.month()) { // 如果是同一个月
      return monday.date()
    } else { // 跨月了，则找上周五
      const friday = theDate.subtract(2, 'day')
      return friday.date()
    }
  } else if (dayOfWeek === 6) { // 周六
    const friday = theDate.subtract(1, 'day')
    if (friday.month() === time.month()) { // 如果是同一个月
      return friday.date()
    } else { // 跨月了，则找下周一
      const monday = theDate.add(2, 'day')
      return monday.date()
    }
  } else { // 工作日
    return theDate.date()
  }
}

/**
 * @Author jiangzhiwei
 * @Date 2023-07-26 10:27:52
 * @Description 指定时间所在月份的第几个星期几是几号。如果没有这一天，则返回undefined
 * @param {object} time 指定时间。dayjs对象
 * @param {number} nth 第几个
 * @param {number} dayOfWeek 星期几。0-6代表周日-周六
 * @return {number | undefined}
 */
export function getNthWeekOfMonth (time, nth, dayOfWeek) {
  /** 本月第一天 */
  const firstDay = time.startOf('month')
  /** 本月第一天是周几 */
  const dayOfWeek2 = firstDay.day()
  /** 一周内相差几天 */
  const diffWeek = dayOfWeek - dayOfWeek2
  /** 一月内与第一天相差几天 */
  const diffDay = diffWeek < 0 ? diffWeek + nth * 7 : diffWeek + (nth - 1) * 7
  /** 该月总天数 */
  const total = getLastDayOfMonth(time)
  if (diffDay > total - 1) { // 超出当月时间
    return undefined
  }
  const day = firstDay.add(diffDay, 'day')
  return day.date()
}

// /**
//  * @Author jiangzhiwei
//  * @Date 2023-07-26 13:42:09
//  * @Description 指定时间所在月份的周中，在两个星期几之间所包含的所有日
//  * @param {object} time 指定时间。dayjs对象
//  * @param {number} dayOfWeek1 第一个星期几。0-6代表周日-周六
//  * @param {number} dayOfWeek2 第二个星期几。0-6代表周日-周六
//  * @return {Array} 日数组
//  */
// export function getBetweenDayOfWeeksOfMonth (time, dayOfWeek1, dayOfWeek2) {
//   /** 一周内的星期范围一共有几天 */
//   const daysOfWeek = dayOfWeek2 - dayOfWeek1 + 1
//   /** 本月总共天数 */
//   const total = time.daysInMonth()
//   /** 本月第一天是星期几 */
//   const firstDay = time.startOf('month').day()
//   /** 第一个星期几与本月第一天相差了几天 */
//   const offset = dayOfWeek1 - firstDay
//   // 一个月最多6周（4周28天，剩余天数分布在首尾各算一周），于是进行六次循环加入这些天
//   const days = []
//   for (let i = 0; i < 6; i++) { // 遍历周
//     for (let j = 0; j < daysOfWeek; j++) { // 遍历区间
//       const day = offset + 1 + i * 7 + j
//       if (day > 0 && day <= total) { // 一个月内的正常日
//         days.push(day)
//       } else if (day > total) {
//         break
//       }
//     }
//   }
//   return days
// }

/**
 * @Author jiangzhiwei
 * @Date 2023-07-31 00:43:13
 * @Description 根据一周的星期几数组获取所在月份的具体日
 * @param {object} time 指定时间。dayjs对象
 * @param {Array<number>} dayOfWeeks 星期几数组。数组中元素的0-6代表周日-周六
 * @return {Array<number>} 日数组
 */
export function getDaysByDayOfWeeks (time, dayOfWeeks) {
  dayOfWeeks.sort((a, b) => a - b) // 排序
  /** 最早的那天是星期几 */
  const dayOfWeek0 = dayOfWeeks[0]
  /** 该月第一个星期几是哪天 */
  const day1 = getFirstWeekOfMonth(time, dayOfWeek0)
  const startDay = day1 - 7 // 从负数开始，确保囊括了第一周的所有天
  /** 本月总共天数 */
  const total = time.daysInMonth()
  // 一个月最多6周（4周28天，剩余天数分布在首尾各算一周），于是进行六次循环加入这些天
  const days = []
  for (let i = 0; i < 6; i++) {
    for (let j = 0; j < dayOfWeeks.length; j++) {
      const dayOfWeek = dayOfWeeks[j]
      const day = startDay + (dayOfWeek - dayOfWeek0) + i * 7
      if (day > 0 && day <= total) { // 一个月内的正常日
        days.push(day)
      } else if (day > total) { // 超过这个月了
        break
      }
    }
  }
  return days
}
